﻿using System;

namespace DotNetRepository.Tools
{
    public class SequentialGuidGen
    {
        // Static members are 'eagerly initialized', that is,
        // immediately when class is loaded for the first time.
        // .NET guarantees thread safety for static initialization
        public static readonly SequentialGuidGen Instance = new SequentialGuidGen();
        private const long TickOffset = -48050543696576512;
        public static readonly DateTime MinTimeStamp = new DateTime(1980,1,1);
        public static readonly DateTime MaxTimeStamp = new DateTime(2208,5,4);

        private readonly PreciseClock _clock;

        private SequentialGuidGen()
        {
            _clock = new PreciseClock(60);
        }

        public Guid NewGuid()
        {
            var timeSeed = _clock.UtcNow.AddTicks(TickOffset);

            byte[] guidBytes = Guid.NewGuid().ToByteArray();
            byte[] dateBytes = BitConverter.GetBytes(timeSeed.Ticks);

            // Replace the first byte with the byte code of BusinessObjectType
            // guidBytes[0] = typeByte;

            // Replace the last 6 bytes with the middle 6 bytes of the DateTime.
            // DateTime is an 8-byte type, so we are dropping the first and last byte.
            // This gives us a DateTime type with accuracy to 0.025 milliseconds (256 ticks)
            // and a range of 1980-01-01 00:00:00:000.0000 to 2208-05-04 23:50:03:792.7936
            for (var i = 1; i <= 6; i++)
            {
                guidBytes[16 - i] = dateBytes[i];
            }
            return new Guid(guidBytes);
        }

        public static DateTime GetTimeStamp(Guid id)
        {
            var dateBytes = new byte[8];
            dateBytes[0] = 0;
            dateBytes[7] = 8;
            byte[] guidBytes = id.ToByteArray();
            for (int i = 1; i <= 6; i++)
            {
                dateBytes[i] = guidBytes[16 - i];
            }

            return new DateTime(BitConverter.ToInt64(dateBytes, 0), DateTimeKind.Utc).AddTicks(TickOffset * -1);
        }
    }
}
